use crate::features::explorer::modelsdbo::{
  EditorDBO, FileDBO, FolderDBO, NewEditorDBO, NewFileDBO, NewFolderDBO, UpdateEditorDBO,
  UpdateFileDBO, UpdateFolderDBO,
};
use crate::features::quizzes::modelsdbo::QuizDBO;
use crate::features::sgraph::{self};
use chrono::prelude::*;
use rbatis::core::db::DBExecResult;
use rbatis::crud::{Skip, CRUD};
use rbatis::executor::RbatisExecutor;
use rbatis::rbatis::Rbatis;
use uuid::Uuid;

/// 通过 id 获取文件夹：Folder
pub async fn find_folder_by_id(rbatis_pool: &Rbatis, id: &str) -> Result<FolderDBO, rbatis::Error> {
  let id_wrapper = rbatis_pool.new_wrapper().eq("id", id);
  let result = rbatis_pool.fetch_by_wrapper::<FolderDBO>(id_wrapper).await;
  result
}

#[py_sql(rbatis, "SELECT f.* FROM folders f WHERE f.pid=#{pid}")]
pub async fn list_sub_folders(rbatis: &Rbatis, pid: &str) -> Option<Vec<FolderDBO>> {
  todo!()
}

#[py_sql(rbatis, "SELECT * from files  WHERE folder_id=#{folder_id}")]
pub async fn list_folder_files(rbatis: &Rbatis, folder_id: &str) -> Option<Vec<FileDBO>> {
  todo!()
}

#[py_sql(rbatis, "SELECT * from quizzes  WHERE folder_id=#{folder_id}")]
pub async fn list_folder_quizzes(rbatis: &Rbatis, folder_id: &str) -> Option<Vec<QuizDBO>> {
  todo!()
}

pub async fn insert_folder(
  rbatis_pool: &Rbatis,
  folder: &NewFolderDBO,
) -> Result<FolderDBO, rbatis::Error> {
  let my_uuid = Uuid::new_v4().to_simple().to_string();
  let folder_id = format!("fldr:{}", &my_uuid);
  let new_folder_dbo = NewFolderDBO {
    id: folder_id.to_owned(),
    ..folder.to_owned()
  };

  let db_result = rbatis_pool
    .save::<NewFolderDBO>(&new_folder_dbo, &[Skip::Value(bson2::Bson::Null)])
    .await;

  if db_result.is_ok() {
    let folder_dbo = find_folder_by_id(rbatis_pool, &folder_id).await;
    if folder_dbo.is_ok() {
      Ok(folder_dbo.unwrap())
    } else {
      Err(rbatis::Error::E(format!(
        "error to find the newly inserted folder by its id={}",
        &folder_id
      )))
    }
  } else {
    Err(rbatis::Error::E(format!(
      "error to insert the folder={:?}",
      new_folder_dbo
    )))
  }
}

/// update folder
pub async fn update_folder(
  rbatis_pool: &Rbatis,
  id: &str,
  update_folder: &UpdateFolderDBO,
) -> Result<u64, rbatis::Error> {
  let id_wrapper = rbatis_pool.new_wrapper().eq("id", id.to_owned());

  let folder_dbo = UpdateFolderDBO {
    ..update_folder.to_owned()
  };

  let updated = rbatis_pool
    .update_by_wrapper(&folder_dbo, id_wrapper, &[Skip::Value(bson2::Bson::Null)])
    .await;

  updated
}

/// 通过 id 获取文件：File
pub async fn find_file_by_id(rbatis_pool: &Rbatis, id: &str) -> Result<FileDBO, rbatis::Error> {
  let id_wrapper = rbatis_pool.new_wrapper().eq("id", id);
  let result = rbatis_pool.fetch_by_wrapper::<FileDBO>(id_wrapper).await;
  result
}

pub async fn insert_file(
  rbatis_pool: &Rbatis,
  file: &NewFileDBO,
) -> Result<FileDBO, rbatis::Error> {
  let my_uuid = Uuid::new_v4().to_simple().to_string();
  let file_id = format!("file:{}", &my_uuid);
  let new_file_dbo = NewFileDBO {
    id: file_id.to_owned(),
    ..file.to_owned()
  };

  let db_result = rbatis_pool
    .save::<NewFileDBO>(&new_file_dbo, &[Skip::Value(bson2::Bson::Null)])
    .await;

  if db_result.is_ok() {
    let file_dbo = find_file_by_id(rbatis_pool, &file_id).await;
    if file_dbo.is_ok() {
      Ok(file_dbo.unwrap())
    } else {
      Err(rbatis::Error::E(format!(
        "error to find the newly inserted file by its id={}",
        &file_id
      )))
    }
  } else {
    Err(rbatis::Error::E(format!(
      "error to insert the file={:?}",
      new_file_dbo
    )))
  }
}

/// update file
pub async fn update_file(
  rbatis_pool: &Rbatis,
  id: &str,
  update_file: &UpdateFileDBO,
) -> Result<u64, rbatis::Error> {
  let id_wrapper = rbatis_pool.new_wrapper().eq("id", id.to_owned());

  let file_dbo = UpdateFileDBO {
    ..update_file.to_owned()
  };

  let updated = rbatis_pool
    .update_by_wrapper(&file_dbo, id_wrapper, &[Skip::Value(bson2::Bson::Null)])
    .await;

  updated
}

/// 通过 id 获取编辑器：Editor
pub async fn find_editor_by_id(rbatis_pool: &Rbatis, id: &str) -> Result<EditorDBO, rbatis::Error> {
  let id_wrapper = rbatis_pool.new_wrapper().eq("id", id);
  let result = rbatis_pool.fetch_by_wrapper::<EditorDBO>(id_wrapper).await;
  result
}

/// update editor
pub async fn update_editor(
  rbatis_pool: &Rbatis,
  id: &str,
  update_editor: &UpdateEditorDBO,
) -> Result<u64, rbatis::Error> {
  let id_wrapper = rbatis_pool.new_wrapper().eq("id", id.to_owned());

  let update_editor_dbo = UpdateEditorDBO {
    ..update_editor.to_owned()
  };

  let updated = rbatis_pool
    .update_by_wrapper(
      &update_editor_dbo,
      id_wrapper,
      &[Skip::Value(bson2::Bson::Null)],
    )
    .await;

  updated
}

pub async fn insert_editor(
  rbatis_pool: &Rbatis,
  editor: &NewEditorDBO,
) -> Result<EditorDBO, rbatis::Error> {
  let editor_id = editor.id.to_owned();
  let new_editor_dbo = NewEditorDBO {
    ..editor.to_owned()
  };

  let db_result = rbatis_pool
    .save::<NewEditorDBO>(&new_editor_dbo, &[Skip::Value(bson2::Bson::Null)])
    .await;

  if db_result.is_ok() {
    let editor_dbo = find_editor_by_id(rbatis_pool, &editor_id).await;
    if editor_dbo.is_ok() {
      Ok(editor_dbo.unwrap())
    } else {
      Err(rbatis::Error::E(format!(
        "error to find the newly inserted editor  by its id={}",
        &editor_id
      )))
    }
  } else {
    Err(rbatis::Error::E(format!(
      "error to insert the editor={:?}",
      new_editor_dbo
    )))
  }
}

/// update or insert editor
pub async fn upsert_editor(
  rbatis_pool: &Rbatis,
  id: &str,
  editor: &UpdateEditorDBO,
) -> Result<EditorDBO, rbatis::Error> {
  let edge_dbo = self::find_editor_by_id(rbatis_pool, id).await;
  if edge_dbo.is_ok() {
    let updated = update_editor(rbatis_pool, id, editor).await;
    println!("upsert_editor updated={:?}", updated);
  } else {
    let e = editor.to_owned();
    let new_dbo = NewEditorDBO {
      id: id.to_owned(),
      title: e.title,
      status: e.status,
      ..Default::default()
    };
    let inserted = insert_editor(rbatis_pool, &new_dbo).await;
    println!("upsert_editor inserted={:?}", inserted);
  }

  self::find_editor_by_id(rbatis_pool, id).await
}
